<?php

/**
 * GenProg Lenguaje de Consultas Basado en SQL
 *
 *
 * @author      Carlos Peña <crash.madover@gmail.com>
 * @package     application.libraries.orm.gpql
 * @copyright   (c) 2013, GenProg
 * @version     v 1.0
 */
include_once 'sql/sql.php';
// Generacion de Sintaxis SQL
include_once 'sql/tabla.php';
include_once 'sql/registro.php';
include_once 'gpdl.php';

// Definicion del Lenguaje y Mapeo

class GPQL extends GPDL {

  /**
   * Instacia por Patrones
   *
   * @access private
   * @var GPQL
   */
  public static $instancia = null;

  // --------------------------------------------------------------------------------

  /**
   * Conexion Base De Datos
   *
   * @var CI_DB
   */
  private $db = null;

  // --------------------------------------------------------------------------------

  /**
   * Modelo
   *
   * @access   private
   * @var      string
   */
  private $Modelo;

  // --------------------------------------------------------------------------------

  /**
   * Objeto
   *
   * @access   private
   * @var      Object      PHP
   */
  private $Objeto;

  // --------------------------------------------------------------------------------

  /**
   * Arreglo de Mysql
   *
   * @access   private
   * @var      array()
   */
  private $oMysql = null;

  // --------------------------------------------------------------------------------

  /**
   * Arreglo de Insert
   *
   * @access   private
   * @var      array()
   */
  private $insertar = array(0 => 'Error');
  private $union = array();
  private $listar;
  private $asignar = '*'; // Equivalente SET
  private $donde = ''; // Equivalente WHERE
  private $consulta = '';

  /**
   * Constructor de la Clase
   *
   * @access      public
   */
  function __construct($sModelo = '', $DB = null) {
    if ($sModelo) {
      $lstModel = explode(',', $sModelo);
      if (count($lstModel) > 1) {
        $this->union = $lstModel;
      }
    }
    $this->Modelo = $sModelo;
    $this->db = $DB;
  }

  /**
   * Obtener Instancia de la Clase Patrones
   *
   * @param       string      Consulta o Modelo
   * @param       object      Clase
   * @return      class
   */
  public static function Cargar($sModelo = '', $DB = null) {
    if (self::$instancia instanceof GPQL) {
      
    } else {
      self::$instancia = new GPQL($sModelo, $DB);
    }
    return self::$instancia;
  }

  // --------------------------------------------------------------------------------

  /**
   * Asginar Modelo a la clase
   *
   * @access      public
   * @param       string      Modelo Tipo CI
   * @return      object      Clase
   */
  public function AsignarModelo($sModelo = '') {
    $this->Modelo = $sModelo;
  }

  // --------------------------------------------------------------------------------

  /**
   * Crear Consultas SQL
   *
   * @access      public
   * @param       string     Modelo Tipo CI
   * @return      SQL
   */
  public function Crear_Consulta($sConsulta = '') {
    $this->consulta = $sConsulta;
    return $this;
  }

  // --------------------------------------------------------------------------------

  /**
   * Crear Tablas MYSQL
   *
   * @access      public
   * @param       string    Modelo
   * @return      TABLA
   */
  public function Tabla($sModelo = '') {
    return Tabla::Cargar($sModelo, $this->db);
  }

  // --------------------------------------------------------------------------------

  /**
   * Obtener Elementos con un Identificador
   *
   * @access      public
   * @param       array()      arreglo Parametros o Identificador
   * @param       boolean      Prefijo _Pref
   * @param       boolean      Modo Vago Evita la recursividad (MODE: LAZY)
   * @return      object       Objeto GPDL Definicion
   */
  public function ObtenerID($arr = null, $bPre = FALSE, $bVago = FALSE, $sWhere = null) {
    $where = '';


    $Definicion = $this->ObtenerMapeoRelacional($arr, $this->db);

    if ($this->Modelo == '') {
      $this->Modelo = $Definicion->mode;
      $where = ' WHERE ' . $Definicion->clave . ' = \'' . $Definicion->valor . '\' ';
    }

    if ($this->Modelo != '') {
      $Class = strtolower($this->Modelo);
      include_once (APPPATH . 'models/' . MODEL . $Class . '.php');
      $MObjeto = new $Class();
      $SQL = new SQL($this->Modelo);

      if (!isset($Definicion->mode)) {
        if (is_array($arr)) {
          $where = ' WHERE ';
          $i = 0;
          foreach ($arr as $sClave => $sValor) {
            if ($i++ == 0) {
              $where .= $sClave . $sValor;
            } else {
              $where .= ' AND ' . $sClave . $sValor;
            }
          }
        }
      }
      if (isset($sWhere))
        $where = ' WHERE ' . $sWhere;
      $sConsulta = $SQL->Generar();
      $sConsulta .= $where;
      //echo $sConsulta;
      $rs = $this->db->query($sConsulta);
      $this->Objeto = (object) array('error' => 1);
      if ($rs->num_rows() > 0) {
        $this->oMysql = $rs->result();
        $this->Objeto = $this->ObtenerObjeto($this->Modelo, 'unico', '', 0, $bPre, $bVago);
        $this->Objeto->error = 0;
      }
      return $this->Objeto;
    }
  }

  function Limpiar_Cache() {
    $this->Objeto = null;
    $this->oMysql = null;
  }

  // --------------------------------------------------------------------------------

  /**
   * Listar Elementos Generales
   *
   * @access      public
   * @param       array()      arreglo Parametros o Identificador
   * @param       boolean      Prefijo _Pref
   * @param       boolean      Modo Vago Evita la recursividad (MODE: LAZY)
   * @return      object       Objeto GPDL Definicion
   */
  public function Listar_Donde($bPre = FALSE, $bVago = FALSE) {




    $SQL = new SQL($this->Modelo);


    $sConsulta = $SQL->Generar($this->asignar);
    $sConsulta .= $this->donde;
    $this->listar = $sConsulta;
    return $this;
  }

  // --------------------------------------------------------------------------------

  /**
   * Salvar O Actualizar un Objeto Persistente a la Base de Datos
   *
   * @access      public
   * @param       array
   *
   *
   */
  public function SalvarOActualizar($aObjeto = array(), $bPrefijo = FALSE) {

    $Mapear = $this->MapearObjeto($aObjeto, $bPrefijo);
    //Se Realizan los Grupos del Insert
    $sSeleccion = '';
    $sActualizar = '';
    $sCambiar = '';
    $sInsertar = '';
    $sValues = '';
    $Persistencia['where'] = '';
    // echo '<pre>';
    //print_r($Mapear);
    //    echo '<br><br><br>';
    $i = 0;
    foreach ($Mapear as $sC => $sV) {
      $Definicion = $this->ObtenerMapeoRelacional($sC, $this->db);
      $sInsertar = '';
      $sValues = '';
      $sCambiar = '';
      if (isset($Definicion->tabl)) {
        $Persistencia = $this->PersistenciaPorColumna($Definicion->tabl, $sV, $this->db);

        $iCantidad = count($sV);
        $sActualizar[$Definicion->tabl] = 'UPDATE ' . $Definicion->tabl . ' SET ';
        //$sSeleccion[$Definicion->tabl] = 'INSERT IGNORE INTO ' . $Definicion->tabl . ' ';
        $sSeleccion[$Definicion->tabl] = 'INSERT INTO ' . $Definicion->tabl . ' ';
        $i = 0;
        foreach ($sV as $sClave => $sValor) {
          ++$i;
          $sValues .= $sClave;
          $sInsertar .= '\'' . $sValor . '\'';
          $sCambiar .= $sClave . '=\'' . $sValor . '\'';
          if ($iCantidad != $i) {
            $sValues .= ',';
            $sInsertar .= ',';
            $sCambiar .= ',';
          }
        }
        $sActualizar[$Definicion->tabl] .= $sCambiar . ' WHERE ' . $Persistencia['where'];
        $sSeleccion[$Definicion->tabl] .= '(' . $sValues . ') VALUES (' . $sInsertar . ') ON DUPLICATE KEY UPDATE ' . $sCambiar . ';';
      }
    }
    if ($Persistencia['where'] != '') {
      $arr = $sActualizar;
      //Update
    } else {
      $arr = $sSeleccion;
      // Insert
    }
    $this->insertar = $arr;
    return $this;
  }

  // ---------------------------------------------------------------------------------

  /**
   *
   */
  public function Comitear() {
    $iCan = count($this->insertar);
    if ($iCan > 0) {
      if ($iCan == 1) {
        foreach ($this->insertar as $sC => $sV) {
          $this->db->query($sV);
        }
      } else {
        $this->db->trans_start();
        foreach ($this->insertar as $sC => $sV) {
          $sInsertar = str_replace("(''", "(LAST_INSERT_ID()", $sV);
          $this->db->query($sInsertar);
        }
        $this->db->trans_complete();
        if ($this->db->trans_status() === FALSE) {
          $this->db->trans_rollback();
        } else {
          $this->db->trans_commit();
        }
      }
    }
    $this->insertar = array();
    echo 'Felicitaciones Proceso Exitoso...';
  }

  // --------------------------------------------------------------------------------

  /**
   * Obtenre Resultados Mysql
   *
   * @param       string      Consulta SQL
   * @return      Mysql       Object
   */
  public function ObtenerResultado($sConsulta = '') {
    $sBuscar = array('MPersona', 'MEmpleado', 'MCargos');
    $sReemplazar = array('Gp_TPersona', 'Gp_TEmpleado', 'Gp_TCargos');

    $sConsulta = str_ireplace($sBuscar, $sReemplazar, $sConsulta);
    $rs = $this->db->query($sConsulta);
    if (count($rs->num_rows() > 0)) {
      return $rs->result();
    }
    return FALSE;
  }

  // ---------------------------------------------------------------------------

  /**
   * Obtener Objeto Recursivo
   *
   * @param       string      Modelo de Objeto GPDL
   * @param       string      Tipo de Operacion Recursiva y/o Unica
   * @param       string      Descripcion del Modelo
   * @param       integer     Posicion de la linea en Mysql por lineas
   * @param       boolean      Prefijo _Pref
   * @param       boolean      Modo Vago Evita la recursividad (MODE: LAZY)
   * @return      object      Objeto Recursivo || Unico
   */
  private function ObtenerObjeto($sModelo = '', $sTipo = '', $dtd = null, $i = 0, $bPre = FALSE, $bVago = FALSE) {
    $obj = array();
    $objAu = array();
    $Clase = strtolower($sModelo);
    include_once (APPPATH . 'models/' . MODEL . $Clase . '.php');

    $DLObjeto = new $Clase();

    //Obetener Definicion del Objeto
    $objeto = $this->ObtenerDefinicion($DLObjeto, $bPre);
    if (isset($dtd))
      $dtd = $sModelo;
    if ($sTipo == 'recursivo' && $bVago != TRUE) {
      $iContador = 1;
      $objetoAuxA = array();
      $objetoAuxB = array();
      foreach ($this->oMysql as $sC => $sV) {
        //De Mysql Al Objeto

        foreach ($objeto as $sClave => $sValor) {
          $objetoAuxA[$sValor] = $sV->$sClave;
        }

        //Comparando Dos Arreglos En caso de serlo no repetirlo
        if (count(array_diff($objetoAuxA, $objetoAuxB)) > 0) {
          $objetoAuxC = null;
          if (isset($DLObjeto->struc['join'])) {
            $iCount = $iContador - 1;
            $this->Recursividad($DLObjeto->struc['join'], $iCount, $objetoAuxC, $bPre, $bVago);
            $objetoAuxD = array_merge($objetoAuxA, $objetoAuxC);
            $obj[$iContador++] = (object) $objetoAuxD;
            $objetoAuxB = (array) $objetoAuxA;
            $objetoAuxC = null;
            $objetoAuxD = null;
          } // Fin del Objeto
        }
        $objetoAuxA = null;
      }
    } else {//En el caso de no ser recursivo
      //De Mysql Al Objeto
      foreach ($objeto as $sClave => $sValor) {
        $objetoAuxA[$sValor] = $this->oMysql[$i]->$sClave;
      }

      $obj = (object) $objetoAuxA;
    }
    if ($sTipo != 'recursivo') {
      if (isset($DLObjeto->struc['join'])) {
        $this->Recursividad($DLObjeto->struc['join'], $i, $obj, $bPre, $bVago);
      }
    }//Fin de Recursivo
    return $obj;
  }

  // ---------------------------------------------------------------------------

  /**
   * La Recursivad Permite Heredar e Implementar ObtenerObjeto
   *
   * @access      public
   * @param       array()       JOIN de los Modelos
   * @param       integer       Controla la Posicion del arreglo Mysql
   * @param       object|array  Valor Por Referencia
   * @return      boolean       Verdadero
   */
  private function Recursividad($join, $i, &$obj, $bPre = FALSE, $bVago = FALSE) {
    $relacion = 'recursivo';
    foreach ((object) $join as $sCla => $sVal) {
      $dtd = $sCla;
      if (isset($sVal['dtd']))
        $dtd = $sVal['dtd'];
      if (isset($sVal['relacion'])) {
        if ($sVal['relacion'] == 'uno-a-uno' || $sVal['relacion'] == 'cero-a-uno')
          $relacion = 'unico';
        if (is_object($obj)) {
          $obj->$dtd = $this->ObtenerObjeto($sCla, $relacion, $dtd, $i, $bPre, $bVago);
        } else {
          $obj[$dtd] = $this->ObtenerObjeto($sCla, $relacion, $dtd, $i, $bPre, $bVago);
        }
      }
    }
    return TRUE;
  }

  // ---------------------------------------------------------------------------

  /**
   * Asignar las columnas
   *
   * @access      public
   * @param       string      Arreglo de columnas
   * @return      string       Arreglo de columnas
   */
  public function Asignar_Columnas($param = '') {
    $Set = '';
    $a = $this->PersistenciaPorTablas($this->db);
    $lista = explode(',', strtolower($param));
    $i = 0;
    $iCant = count($lista);
    foreach ($lista as $sC => $sV) {
      ++$i;
      $Set .= preg_replace($a['tabla_prefijo'], $a['campo_prefijo'], trim($sV));
      if ($iCant != $i) {
        $Set .= ', ';
      }
    }
    $this->asignar = $Set;
    return $this;
  }

  /**
   * Donde las columnas
   *
   * @access      public
   * @param       string      Arreglo de columnas
   * @return      string       Arreglo de columnas
   */
  public function Donde_Columnas($param = '') {
    $Set = '';
    $a = $this->PersistenciaPorTablas($this->db);
    $lista = explode(' ', strtolower($param));
    $i = 0;
    $iCant = count($lista);
    foreach ($lista as $sC => $sV) {
      ++$i;
      $Set .= preg_replace($a['tabla_prefijo'], $a['campo_prefijo'], trim($sV));

      if ($iCant != $i) {
        $Set .= ' ';
      }
    }
    $this->donde = ' WHERE ' . $Set;
    return $this;
  }

  public function ListarDonde_DEMO($arr = array()) {

    $sRefTabla = '';
    $Donde = '';
    $sCrearJoin = '';
    $a = $this->PersistenciaPorTablas($this->db);

    $lista = explode(',', strtolower($this->Modelo));
    foreach ($lista as $sC => $sV) {
      $Tabla[] = preg_replace($a['modelo'], $a['tabla'], trim($sV));
    }
    //JOIN
    $i = 0;
    $iCant = count($Tabla);
    foreach ($Tabla as $sC => $sV) {
      $sJoin = 'SELECT * FROM Gp_SClavesForaneas WHERE tabla =\'' . $sV . '\'';
      $rs = $this->db->query($sJoin);
      if ($rs->num_rows() > 0) {
        $rsC = $rs->result();
        foreach ($rsC as $Clave => $sValor) {
          $sCrearJoin .= ' JOIN ' . $sValor->ref_tabla . ' ON ' . $sValor->ref_tabla . '.' . $sValor->ref_columna . '=' . $sValor->tabla . '.' . $sValor->columna;
          $sRefTabla = $sValor->ref_tabla;
        }
      } else {
        $sRefTabla = $sV;
      }
    }

    //WHERE
    $i = 0;
    $iCant = count($arr);
    foreach ($arr as $sC => $sV) {
      ++$i;

      $Donde .= preg_replace($a['tabla_prefijo'], $a['campo_prefijo'], $sC);
      $Donde .= '=\'' . $sV . '\'';
      if ($iCant != $i) {
        $Donde .= ' AND ';
      }
    }
    $Where = '';
    if ($Donde != '') {
      $Where = ' WHERE ' . $Donde;
    }

    $this->listar = 'SELECT ' . $this->asignar . ' FROM ' . $sRefTabla . $sCrearJoin . $Where;
    return $this;
  }

  function Listar_Todo() {
    $sRefTabla = '';
    $a = $this->PersistenciaPorTablas($this->db);
    $lista = explode(',', strtolower($this->Modelo));
    foreach ($lista as $sC => $sV) {
      $sRefTabla[] = preg_replace($a['modelo'], $a['tabla'], trim($sV));
    }
    $this->listar = 'SELECT ' . $this->asignar . ' FROM ' . $sRefTabla[0] . $this->donde;
    return $this;
  }

  // ---------------------------------------------------------------------------


  function Ver_Consulta() {
    $arr['Isertar'] = $this->insertar;
    $arr['Listar'] = $this->listar;
    echo '<pre>';
    print_r($arr);
    echo '</pre>';
    return $this;
  }

  function Ejecutar($sConsulta = null, $insert = null) {
    $ejecutar = array();
    $err = 1;
    if (isset($sConsulta)) {
      $rs = $this->db->query($sConsulta);
      if (!isset($insert)) {
        if ($rs->num_rows() > 0) {
          $ejecutar['err'] = 0;
          $ejecutar['rs'] = $rs->result();
        } else {
            
          $ejecutar['rs'] = array();
          $ejecutar['err'] = 1;
        }
      }
    } else {
      if ($this->listar != '') {
        $rs = $this->db->query($this->listar);
        $err = 0;
        $ejecutar['rs'] = $rs->result();
      } else {
        $err = 1;
      }
    }
    $ejecutar['err'] = $err;
    return $ejecutar;
  }

}

?>
